package com.chenfan.mcn.mq.producer;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chenfan.mcn.clients.baseinfo.BaseCustomer;
import com.chenfan.mcn.clients.baseinfo.BaseInfoClient;
import com.chenfan.mcn.clients.baseinfo.DescByCodeDTO;
import com.chenfan.mcn.clients.finance.FinanceClient;
import com.chenfan.mcn.clients.finance.McnChargeCommonVO;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.constant.SeparatorCst;
import com.chenfan.mcn.dao.CalculationDevidedMapper;
import com.chenfan.mcn.dao.ContractExcutionOrderMapper;
import com.chenfan.mcn.dao.IncomeContractMapper;
import com.chenfan.mcn.dao.StarBasicDataMapper;
import com.chenfan.mcn.enums.*;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.model.CalculationDevided;
import com.chenfan.mcn.model.ContractExcutionOrder;
import com.chenfan.mcn.model.IncomeContract;
import com.chenfan.mcn.model.StarBasicData;
import com.chenfan.mcn.mq.AbstractMessageProducer;
import com.chenfan.mcn.mq.MessageProducer;
import com.chenfan.mcn.mq.message.ChargeAutoTaxInvoiceMessage;
import com.chenfan.mcn.mq.message.CfChargeCommonMessage;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: tangwei
 * Date:     2021/3/3 18:14
 * Description: 收入合同审批通过生成费用推送财务系统
 */

@Slf4j
@Service
public class IncomeContractMessageProducer extends AbstractMessageProducer implements MessageProducer<ImmutablePair<Long, Boolean>> {

    @Autowired
    private IncomeContractMapper incomeContractMapper;

    @Autowired
    private ContractExcutionOrderMapper contractExcutionOrderMapper;

    @Autowired
    private BaseInfoClient baseInfoClient;

    @Autowired
    private FinanceClient financeClient;

    @Autowired
    private CalculationDevidedMapper calculationDevidedMapper;

    @Autowired
    private StarBasicDataMapper starBasicDataMapper;

    @Async
    @Override
    public void asyncSendRecordToMq(ImmutablePair<Long, Boolean> immutablePair) {
        Long incomeContractId = immutablePair.left;
        Boolean repushMq = ObjectUtils.defaultIfNull(immutablePair.right, Boolean.FALSE);
        log.info("进入收入合同推送MQ方法==》incomeContractId={}", incomeContractId);
        IncomeContract incomeContract = incomeContractMapper.selectById(incomeContractId);
        BusinessAssert.notNull(incomeContract, McnErrorCode.INCOME_NOT_EXISTS);
        log.info("收入合同数据:{}", JSON.toJSONString(incomeContract));
        Map<Long, BaseCustomer> customerMap = baseInfoClient.getMcnCustomerByIds(Collections.singletonList(incomeContract.getCustomerId())).getObj();
        LambdaQueryWrapper<ContractExcutionOrder> queryWrapper = Wrappers.lambdaQuery(ContractExcutionOrder.class)
                .eq(ContractExcutionOrder::getIncomeContractId, incomeContract.getId())
                .ne(ContractExcutionOrder::getApprovalStatus, ExcutionApprovalStatusEnum.INVALID.code())
                .eq(ContractExcutionOrder::getIsDelete, NumberCst.ZERO);
        List<Integer> existChargeTypeList = null;
        if (repushMq.booleanValue()) {
            List<McnChargeCommonVO> mcnChargeCommonVOList = CommonUtil.defaultList(financeClient.getMcnCharge(incomeContract.getContractCode()).getObj());
            if (CollectionUtils.isNotEmpty(mcnChargeCommonVOList)) {
                List<String> executeCodeList = mcnChargeCommonVOList.stream().filter(e -> !NumberCst.FIVE.equals(e.getCheckStatus()))
                        .map(McnChargeCommonVO::getChargeSourceDetail).filter(Objects::nonNull).distinct().collect(Collectors.toList());
                queryWrapper.notIn(CollectionUtils.isNotEmpty(executeCodeList), ContractExcutionOrder::getExcuteCode, executeCodeList);
                existChargeTypeList = mcnChargeCommonVOList.stream().filter(e -> !NumberCst.FIVE.equals(e.getCheckStatus()))
                        .map(McnChargeCommonVO::getChargeType).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            }
            queryWrapper.and((wrapper) -> wrapper.isNull(ContractExcutionOrder::getContractAgreementId).or()
                    .exists("SELECT 1 FROM income_contract_agreement t WHERE t.is_delete =0 AND t.id = contract_agreement_id AND t.approval_status = 2")
            );
        }
        List<ContractExcutionOrder> excutionOrderList = contractExcutionOrderMapper.selectList(queryWrapper);
        Map<String, String> orderPlatformMap = this.getOrderPlatformMap(excutionOrderList);
        List<CfChargeCommonMessage> chargeList = new ArrayList<>();
        if (IncomeContractTypeEnum.isLiveContract(incomeContract.getContractType())) {
            /**
             * 直播合同推送费用(V2.0.1版本更名为直播框架合同)
             * 1、框架合同审批通过，则根据参与保比的【直播收入】+不参与保比的【直播收入】+参与保比的其他收入+不参与保比的【其他收入】
             * 这四个字段之和（如果为0，则不生成费用），生成一笔【框架合同】费，费用种类为【框架合同费】，结算主体为框架合同的客户。
             * 生成的费用种类为【框架直播合同费】（新增的费用种类）
             * 历史数据：原【坑位费】、【 保ROI服务费】中的数据刷入【框架直播合同费】
             * 其他费用字段生成逻辑跟以前【坑位费】一致。
             * 3、如果有执行单，则按照执行单维度生成应收费用。如果执行单总金额为0，则不生成费用仍需生成费用（确保金额为0的费用在财务模块中能正常走完全流程），生成的费用种类为【框架广告合同费】（”直播广告费“更名而来）。
             */
            // if(CollectionUtils.isNotEmpty(excutionOrderList) && NumberCst.ONE.equals(incomeContract.getContainsAdvertising())) {
            //     excutionOrderList.forEach(excutionOrder -> {
            //         chargeList.add(this.buildChargeMessage(incomeContract, excutionOrder, customerMap, orderPlatformMap));
            //     });
            // }
            if (CollectionUtils.isNotEmpty(excutionOrderList)) {
                for (ContractExcutionOrder excutionOrder : excutionOrderList) {
                    if(ObjectUtils.defaultIfNull(excutionOrder.getPlatformReport(), Boolean.FALSE).booleanValue() && NumberCst.ZERO.equals(excutionOrder.getInSystemAdmin())) {
                        //框架合同-是平台下单-未入驻红人
                        if(repushMq.booleanValue()) {
                            this.addReceivableFeeIfNecessary(FinanceChargeTypeEnum.NO_STAR_INCOME, chargeList, excutionOrder, incomeContract);
                        }
                        continue;
                    }
                    chargeList.add(this.buildChargeMessage(incomeContract, excutionOrder, customerMap, orderPlatformMap));
                }
            }
            //无广告收入
            existChargeTypeList = CommonUtil.defaultList(existChargeTypeList);
            //参与保比的【直播收入】
            BigDecimal roiLiveAmt = Objects.isNull(incomeContract.getRoiLiveAmt()) ? BigDecimal.ZERO : incomeContract.getRoiLiveAmt();
            //不参与保比的【直播收入】
            BigDecimal noroiLiveAmt = Objects.isNull(incomeContract.getNoroiLiveAmt()) ? BigDecimal.ZERO : incomeContract.getNoroiLiveAmt();
            //参与保比的其他收入
            BigDecimal roiOhterAmt = Objects.isNull(incomeContract.getRoiOhterAmt()) ? BigDecimal.ZERO : incomeContract.getRoiOhterAmt();
            //不参与保比的【其他收入】
            BigDecimal noroiOhterAmt = Objects.isNull(incomeContract.getNoroiOhterAmt()) ? BigDecimal.ZERO : incomeContract.getNoroiOhterAmt();
            //计算参与保比的【直播收入】+不参与保比的【直播收入】+参与保比的其他收入+不参与保比的【其他收入】
            BigDecimal total = roiLiveAmt.add(noroiLiveAmt).add(roiOhterAmt).add(noroiOhterAmt);
            //生成框架直播合同费
            if(CommonUtil.greaterThanZero(total)) {
                this.buildLiveChargeMessage(chargeList, existChargeTypeList, incomeContract, customerMap, FinanceChargeTypeEnum.FRAMEWORK_LIVE_CONTRACT_CHARGE, total);
            }
            //生成线下佣金费
            if (OfflineCommissionSettleStatusEnum.isPassed(incomeContract.getOfflineCommissionSettleStatus()) &&
                    CommonUtil.greaterThanZero(incomeContract.getOfflineCommissionAmount())) {
                this.buildLiveChargeMessage(chargeList, existChargeTypeList, incomeContract, customerMap, FinanceChargeTypeEnum.OFFLINE_COMMISSION_CHARGE, incomeContract.getOfflineCommissionAmount());
            }
        } else {
            if (CollectionUtils.isNotEmpty(excutionOrderList)) {
                for (ContractExcutionOrder excutionOrder : excutionOrderList) {
                    //形式合同-未入驻红人
                    if (IncomeContractTypeEnum.isFormalContract(incomeContract.getContractType()) && NumberCst.ZERO.equals(excutionOrder.getInSystemAdmin())) {
                        //如果手动生成，后置费用改成前置
                        if(repushMq.booleanValue()) {
                            this.addReceivableFeeIfNecessary(FinanceChargeTypeEnum.NO_STAR_INCOME, chargeList, excutionOrder, incomeContract);
                        }
                        continue;
                    }
                    chargeList.add(this.buildChargeMessage(incomeContract, excutionOrder, customerMap, orderPlatformMap));
                }
            }
        }
        if (CollectionUtils.isEmpty(chargeList)) {
            log.info("暂无费用消息可发：incomeContractId={}", incomeContractId);
            return;
        }
        if (IncomeContractTypeEnum.isLiveContract(incomeContract.getContractType()) || repushMq.booleanValue()) {
            chargeList.forEach(cfChargeCommonMessage -> {
                this.doSendRecordToMq(incomeContract.getContractCode(), cfChargeCommonMessage, this.financeTopic);
            });
        } else {
            this.doSendRecordToMq(incomeContract.getContractCode(), this.buildAutoTaxInvoiceMessage(incomeContract, customerMap, chargeList), this.financeTopic);
        }
        //检查费用生成情况
        if(!repushMq.booleanValue()) {
            this.checkCharge(incomeContract.getContractCode());
        }
    }

    /**
     * 构建收入合同费用消息
     *
     * @param incomeContract
     * @param excutionOrder
     * @param customerMap
     * @return
     */
    private CfChargeCommonMessage buildChargeMessage(IncomeContract incomeContract, ContractExcutionOrder excutionOrder, Map<Long, BaseCustomer> customerMap, Map<String, String> orderPlatformMap) {
        CfChargeCommonMessage chargeMessage = new CfChargeCommonMessage();
        if (IncomeContractTypeEnum.isFormalContract(incomeContract.getContractType())) {
            chargeMessage.setChargeType(FinanceChargeTypeEnum.YES_FORMAL_CONTRACT_CHARGE.getType());
            chargeMessage.setAccountId(null);
            chargeMessage.setBalance(orderPlatformMap.get(excutionOrder.getOrderPlatform()));
        } else if (IncomeContractTypeEnum.isLiveContract(incomeContract.getContractType())) {
            if(ObjectUtils.defaultIfNull(excutionOrder.getPlatformReport(), Boolean.FALSE).booleanValue() && NumberCst.ONE.equals(excutionOrder.getInSystemAdmin())) {
                chargeMessage.setChargeType(FinanceChargeTypeEnum.ONLINE_YES_ADVERTISING_CHARGE.getType());
                chargeMessage.setBalance(orderPlatformMap.get(excutionOrder.getOrderPlatform()));
            } else {
                chargeMessage.setChargeType(FinanceChargeTypeEnum.OFFLINE_ADVERTISING_CHARGE.getType());
                chargeMessage.setBalance(customerMap.get(incomeContract.getCustomerId()).getCustomerNameC());
            }
            chargeMessage.setAccountId(null);
        } else {
            chargeMessage.setChargeType(FinanceChargeTypeEnum.INCOME_CONTRACT_CHARGE.getType());
            chargeMessage.setAccountId(incomeContract.getCustomerBillingId());
            chargeMessage.setBalance(customerMap.get(incomeContract.getCustomerId()).getCustomerNameC());
        }
        chargeMessage.setBalanceType(NumberCst.TWO);
        chargeMessage.setArapType(FinanceArapTypeEnum.RECEIVE.getType());
        chargeMessage.setChargeSourceCode(incomeContract.getContractCode());
        chargeMessage.setChargeSourceDetail(excutionOrder.getExcuteCode());
        chargeMessage.setChargeSourceDetail2(excutionOrder.getPlatformOrderCode());
        chargeMessage.setAmountPp(excutionOrder.getActualQuotedPrice());
        chargeMessage.setPricePp(excutionOrder.getActualQuotedPrice());

        chargeMessage.setFinanceEntity(incomeContract.getCompanyContractSubject());
        chargeMessage.setCreateBy(incomeContract.getCreateBy());
        chargeMessage.setCreateName(incomeContract.getCreateName());
        chargeMessage.setCreateDate(new Date());
        chargeMessage.setUpdateBy(incomeContract.getCreateBy());
        chargeMessage.setUpdateDate(chargeMessage.getCreateDate());
        chargeMessage.setUpdateName(incomeContract.getCreateName());
        chargeMessage.setTenantId(incomeContract.getTenantId());
        chargeMessage.setCompanyId(incomeContract.getCompanyId());
        chargeMessage.setSettTemplate(null);
        chargeMessage.setTaxRate(BigDecimal.ZERO);
        if (InvoiceFormEnum.NO_NEED_INVOICE.getType().equals(incomeContract.getInvoiceForm())) {
            chargeMessage.setAccountId(null);
        }
        return chargeMessage;
    }

    /**
     * 构建直播生成合同费用
     *
     * @param chargeList
     * @param existChargeTypeList
     * @param incomeContract
     * @param customerMap
     * @param chargeTypeEnum
     * @param feeAmount
     */
    private void buildLiveChargeMessage(List<CfChargeCommonMessage> chargeList, List<Integer> existChargeTypeList, IncomeContract incomeContract, Map<Long, BaseCustomer> customerMap, FinanceChargeTypeEnum chargeTypeEnum, BigDecimal feeAmount) {
        if (existChargeTypeList.contains(chargeTypeEnum.getType())) {
            return;
        }
        CfChargeCommonMessage chargeMessage = new CfChargeCommonMessage();
        chargeMessage.setChargeType(chargeTypeEnum.getType());
        chargeMessage.setArapType(FinanceArapTypeEnum.RECEIVE.getType());
        chargeMessage.setChargeSourceCode(incomeContract.getContractCode());
        chargeMessage.setAmountPp(feeAmount);
        chargeMessage.setPricePp(feeAmount);
        chargeMessage.setBalance(customerMap.get(incomeContract.getCustomerId()).getCustomerNameC());
        chargeMessage.setFinanceEntity(incomeContract.getCompanyContractSubject());
        chargeMessage.setCreateBy(incomeContract.getCreateBy());
        chargeMessage.setCreateName(incomeContract.getCreateName());
        chargeMessage.setCreateDate(new Date());
        chargeMessage.setUpdateBy(incomeContract.getCreateBy());
        chargeMessage.setUpdateDate(chargeMessage.getCreateDate());
        chargeMessage.setUpdateName(incomeContract.getCreateName());
        chargeMessage.setTenantId(incomeContract.getTenantId());
        chargeMessage.setCompanyId(incomeContract.getCompanyId());
        chargeMessage.setSettTemplate(null);
        chargeMessage.setTaxRate(BigDecimal.ZERO);
        /**
         * version:V1.16.1: 新增【财务信息ID+结算主体类型】
         */
        if (InvoiceFormEnum.NO_NEED_INVOICE.getType().equals(incomeContract.getInvoiceForm())) {
            chargeMessage.setAccountId(null);
        } else {
            chargeMessage.setAccountId(incomeContract.getCustomerBillingId());
        }
        chargeMessage.setBalanceType(NumberCst.TWO);
        chargeList.add(chargeMessage);
    }


    /**
     * 构建应收费用
     *
     * @param chargeTypeEnum
     * @param chargeList
     * @param excutionOrder
     * @param incomeContract
     * @return
     */
    private boolean addReceivableFeeIfNecessary(FinanceChargeTypeEnum chargeTypeEnum, List<CfChargeCommonMessage> chargeList, ContractExcutionOrder excutionOrder, IncomeContract incomeContract) {
        if (!ExcutionApprovalStatusEnum.isPassed(excutionOrder.getApprovalStatus())) {
            return false;
        }
        CalculationDevided calculationDevided = calculationDevidedMapper.selectOne(Wrappers.lambdaQuery(CalculationDevided.class)
                .eq(CalculationDevided::getExcutionOrderId, excutionOrder.getId()).eq(CalculationDevided::getIsDelete, NumberCst.ZERO));
        if (Objects.isNull(calculationDevided)) {
            return false;
        }
        if(IncomeContractTypeEnum.isFormalContract(incomeContract.getContractType())
                && ObjectUtils.defaultIfNull(calculationDevided.getAmountReceivable(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) <= 0) {
            return false;
        }
        StarBasicData starBasicData = starBasicDataMapper.selectById(excutionOrder.getStarId());
        if (Objects.isNull(starBasicData)) {
            return false;
        }
        if (!SettlementMethodEnum.isReceivePayment(calculationDevided.getSettlementMethod())) {
            return false;
        }
        CfChargeCommonMessage chargeMessage = new CfChargeCommonMessage();
        chargeMessage.setChargeType(chargeTypeEnum.getType());
        chargeMessage.setAccountId(null);
        chargeMessage.setBalance(starBasicData.getStarNickname());
        chargeMessage.setBalanceType(NumberCst.ONE);
        chargeMessage.setArapType(FinanceArapTypeEnum.RECEIVE.getType());
        chargeMessage.setChargeSourceCode(incomeContract.getContractCode());
        chargeMessage.setChargeSourceDetail(excutionOrder.getExcuteCode());
        chargeMessage.setChargeSourceDetail2(excutionOrder.getPlatformOrderCode());
        chargeMessage.setAmountPp(calculationDevided.getAmountReceivable());
        chargeMessage.setPricePp(calculationDevided.getAmountReceivable());

        chargeMessage.setFinanceEntity(incomeContract.getCompanyContractSubject());
        chargeMessage.setCreateBy(incomeContract.getCreateBy());
        chargeMessage.setCreateName(incomeContract.getCreateName());
        chargeMessage.setCreateDate(new Date());
        chargeMessage.setUpdateBy(incomeContract.getCreateBy());
        chargeMessage.setUpdateDate(chargeMessage.getCreateDate());
        chargeMessage.setUpdateName(incomeContract.getCreateName());
        chargeMessage.setTenantId(incomeContract.getTenantId());
        chargeMessage.setCompanyId(incomeContract.getCompanyId());
        chargeMessage.setSettTemplate(null);
        chargeMessage.setTaxRate(BigDecimal.ZERO);
        if (InvoiceFormEnum.NO_NEED_INVOICE.getType().equals(incomeContract.getInvoiceForm())) {
            chargeMessage.setAccountId(null);
        }
        chargeList.add(chargeMessage);
        return true;
    }

    /**
     * 自动开票
     *
     * @return
     */
    private ChargeAutoTaxInvoiceMessage buildAutoTaxInvoiceMessage(IncomeContract incomeContract, Map<Long, BaseCustomer> customerMap, List<CfChargeCommonMessage> chargeList) {
        ChargeAutoTaxInvoiceMessage chargeAutoTaxInvoiceMessage = new ChargeAutoTaxInvoiceMessage();
        chargeAutoTaxInvoiceMessage.setBalance(chargeList.get(0).getBalance());
        chargeAutoTaxInvoiceMessage.setCreateBy(incomeContract.getCreateBy());
        chargeAutoTaxInvoiceMessage.setCreateName(incomeContract.getCreateName());
        chargeAutoTaxInvoiceMessage.setCreateDate(new Date());
        chargeAutoTaxInvoiceMessage.setUpdateBy(incomeContract.getCreateBy());
        chargeAutoTaxInvoiceMessage.setUpdateDate(incomeContract.getCreateDate());
        chargeAutoTaxInvoiceMessage.setUpdateName(incomeContract.getCreateName());
        chargeAutoTaxInvoiceMessage.setTenantId(incomeContract.getTenantId());
        chargeAutoTaxInvoiceMessage.setCompanyId(incomeContract.getCompanyId());

        if (InvoiceFormEnum.NO_NEED_INVOICE.getType().equals(incomeContract.getInvoiceForm())) {
            chargeAutoTaxInvoiceMessage.setAccountId(null);
        } else {
            chargeAutoTaxInvoiceMessage.setAccountId(incomeContract.getCustomerBillingId());
        }
        chargeAutoTaxInvoiceMessage.setInvoiceContent(incomeContract.getInvoiceContent());
        chargeAutoTaxInvoiceMessage.setInvoiceForm(InvoiceFormEnum.getDesc(incomeContract.getInvoiceForm()));
        chargeAutoTaxInvoiceMessage.setTaxInvoiceType(incomeContract.getInvoiceType());
        chargeAutoTaxInvoiceMessage.setChargeInfos(chargeList);
        return chargeAutoTaxInvoiceMessage;
    }

    /**
     * 获取订单平台翻译Mao
     *
     * @param excutionOrderList
     * @return
     */
    private Map<String, String> getOrderPlatformMap(List<ContractExcutionOrder> excutionOrderList) {
        if (CollectionUtils.isNotEmpty(excutionOrderList)) {
            List<String> orderPlatform = excutionOrderList.stream().filter(e -> StringUtils.isNotBlank(e.getOrderPlatform()))
                    .map(ContractExcutionOrder::getOrderPlatform).distinct().collect(Collectors.toList());
            Map<String, String> dictsNameMap = baseInfoClient.getDescByCode(new DescByCodeDTO()
                    .setProfileCodes(Arrays.asList("formal_contract"))
                    .setDictsCodes(orderPlatform)).getObj();
            return orderPlatform.stream().collect(Collectors.toMap(k -> k, v -> dictsNameMap.get("formal_contract" + SeparatorCst.SEMICOLON + v)));
        }
        return new HashMap<>(0);
    }
}
